package com.keqiang.chart.impl.pie.impl.pseudo3d

import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import com.keqiang.chart.animation.Animator
import com.keqiang.chart.impl.pie.impl.normal.PieEntity
import com.keqiang.chart.impl.pie.render.BaseDataRender
import com.keqiang.chart.utils.ObjectPool
import com.keqiang.chart.utils.draw3DArcBottom
import com.keqiang.chart.utils.draw3DArcShadow
import com.keqiang.chart.utils.draw3DArcTop
import com.keqiang.chart.utils.to360Range
import com.keqiang.chart.utils.toRadian
import kotlin.math.sin

/**
 * 饼状图数据绘制器
 *
 * @author Created by wanggaowan on 2022/8/30 16:30
 */
open class PieChart3DDataRender(chart: PieChart3D) : BaseDataRender<PieChart3D>(chart) {

    override fun onDraw(canvas: Canvas, startAngle: Float) {
        val paint = ObjectPool.obtainPaint<Paint>()
        val (centerX, centerY) = chart.getCenter()
        drawBg(canvas, paint, centerX, centerY)

        if (chart.data.isNullOrEmpty()) {
            ObjectPool.recyclePaint(paint)
            return
        }

        var totalValue = 0f
        val dataList = mutableListOf<PieEntity>()
        chart.data?.let {
            dataList.addAll(it)
        }

        dataList.forEach {
            if (it.isDraw && it.value > 0) {
                totalValue += it.value
            }
        }

        drawData(canvas, paint, centerX, centerY, totalValue, startAngle, dataList, chart.animator)
        ObjectPool.recyclePaint(paint)
    }

    /**
     * 绘制背景
     *
     * @param centerX 圆柱体底部椭圆的圆心X轴坐标
     * @param centerY 圆柱体底部椭圆的圆心Y轴坐标
     *
     */
    protected open fun drawBg(canvas: Canvas, paint: Paint, centerX: Float, centerY: Float) {
        val alpha = Color.alpha(chart.baseMapColor)
        if (alpha == 0) {
            return
        }

        val flipAngle = chart.getDrawFlipAngle()
        val height = chart.getDrawHeight()
        canvas.draw3DArcShadow(paint, centerX, centerY + height, chart.radius, height, flipAngle, 0f, 360f, chart.baseMapColor, drawTop = true)
        canvas.draw3DArcBottom(paint, centerX, centerY + height, chart.radius, chart.innerRadius, height, flipAngle, 0f, 360f, chart.baseMapColor)
        canvas.draw3DArcShadow(paint, centerX, centerY + height, chart.radius, height, flipAngle, 0f, 360f, chart.baseMapColor)
        canvas.draw3DArcTop(paint, centerX, centerY, chart.radius, chart.innerRadius, flipAngle, 0f, 360f, chart.baseMapColor)
    }

    /**
     * 绘制数据内容
     */
    protected open fun drawData(canvas: Canvas, paint: Paint, centerX: Float, centerY: Float, totalValue: Float, startAngle: Float,
                                dataList: List<PieEntity>, animator: Animator?) {
        paint.style = Paint.Style.FILL
        val animProgress = animator?.getProgress() ?: 1f
        val touchData = chart.touchHandler?.touchData
        val highLightRender = chart.highLightRender
        // 偏移角度，也是当前需要绘制扇形的起始角度
        var offsetDegrees = startAngle
        // 记录超过240°的实体，超过240°的扇形侧边存在被下一个扇形侧边遮挡的问题，因此需要特殊处理
        var over240Entity: PieEntity? = null
        for (index in dataList.indices) {
            val data = dataList[index]
            if (!data.isDraw) {
                continue
            }

            val degrees = if (totalValue == 0f) 0f else data.value / totalValue * 360 * animProgress

            data.drawStartAngle = offsetDegrees
            data.drawSweepAngle = degrees
            if (data.drawSweepAngle >= 240) {
                over240Entity = data
            }

            if (touchData == data && highLightRender != null) {
                data.drawRadius = highLightRender.getRadius(data)
                val (dx, dy) = highLightRender.getOffset(data)
                data.offsetDx = dx
                data.offsetDy = dy
            } else {
                data.drawRadius = chart.radius + data.addedRadius
                data.offsetDx = 0f
                data.offsetDy = 0f
            }

            offsetDegrees += degrees
        }

        // 3D饼状图需要分四次绘制，分上下两部分，这样分批绘制主要是解决扇形直接相互遮挡问题
        // 第一部分，第一步绘制上半部分外圆半径阴影，第二步绘制底部扇形及开始和结束位置的矩形侧面
        // 第二部分，第一步绘制下班部分外圆半径阴影，第二步绘制顶部扇形

        // y轴坐标越小的饼状图，需要越早绘制，如果放到比其y轴坐标大的饼状图之后绘制，会遮挡其它饼状图
        // 这一步很重要
        // 此排序用于绘制第一部分
        var sortList = dataList.sortedWith { o1, o2 ->
            val o1Top =
                sin(getMinYAngle(o1.drawStartAngle, o1.drawSweepAngle).toRadian)
            val o2Top =
                sin(getMinYAngle(o2.drawStartAngle, o2.drawSweepAngle).toRadian)

            if (o1Top > o2Top) {
                1
            } else if (o1Top == o2Top) {
                0
            } else {
                -1
            }
        }

        val sliceSpace = if (sortList.size == 1) 0f else chart.sliceSpace
        val flipAngle = chart.getDrawFlipAngle()
        val height = chart.getDrawHeight()
        for (j in 0 until 2) {
            for (index in sortList.indices) {
                val data = sortList[index]
                if (!data.isDraw || data.drawSweepAngle == 0f) {
                    continue
                }

                if (data == touchData && highLightRender != null) {
                    highLightRender.onDraw3D(canvas, data.drawStartAngle, data.drawSweepAngle, data, false, j == 1, j == 0, false)
                    continue
                }

                if (j == 0) {
                    canvas.draw3DArcShadow(paint, centerX, centerY + height, data.drawRadius, height, flipAngle,
                        data.drawStartAngle, data.drawSweepAngle, data.color, data.offsetDx, data.offsetDy, sliceSpace, drawTop = true)
                } else {
                    canvas.draw3DArcBottom(paint, centerX, centerY + height, data.drawRadius, chart.innerRadius, height,
                        flipAngle, data.drawStartAngle, data.drawSweepAngle, data.color, data.offsetDx, data.offsetDy, sliceSpace)
                    if (index > 0 && over240Entity != null) {
                        val endAngle =
                            (over240Entity.drawStartAngle + over240Entity.drawSweepAngle).to360Range
                        if (endAngle > 180 && endAngle < 240) {
                            val originalIndex = dataList.indexOf(over240Entity)
                            val originalIndex2 = dataList.indexOf(data)
                            if (originalIndex2 == originalIndex + 1 && sortList.indexOf(over240Entity) < index) {
                                // 超过240°的扇形，结束位置侧边存在被下一个扇形遮挡问题，因此在绘制下一个扇形前，重新绘制结束位置往前10°区域的扇形
                                if (over240Entity == touchData && highLightRender != null) {
                                    highLightRender.onDraw3D(canvas, over240Entity.drawStartAngle + over240Entity.drawSweepAngle - 60f, 60f, over240Entity,
                                        drawTop = false, drawBottom = true, drawShadowTop = false, drawShadowBottom = false)
                                } else {
                                    canvas.draw3DArcBottom(paint, centerX, centerY + height, over240Entity.drawRadius, chart.innerRadius, height,
                                        flipAngle, over240Entity.drawStartAngle + over240Entity.drawSweepAngle - 60f, 60f, over240Entity.color,
                                        over240Entity.offsetDx, over240Entity.offsetDy, sliceSpace)
                                }
                            }
                        }
                    }
                }
            }
        }

        // 这一步排序很重要，否则绘制会出现扇形直接被遮挡问题。
        // 此排序用于绘制第二部分
        sortList = dataList.sortedWith { o1, o2 ->
            if (o1.drawRadius > o2.drawRadius) {
                1
            } else if (o1.drawRadius == o2.drawRadius) {
                0
            } else {
                -1
            }
        }

        for (j in 0 until 2) {
            for (index in sortList.indices) {
                val data = sortList[index]
                if (!data.isDraw || data.drawSweepAngle == 0f) {
                    continue
                }

                if (data == touchData && highLightRender != null) {
                    highLightRender.onDraw3D(canvas, data.drawStartAngle, data.drawSweepAngle, data,
                        j == 1, drawBottom = false, drawShadowTop = false, drawShadowBottom = j == 0)
                    continue
                }

                if (j == 0) {
                    canvas.draw3DArcShadow(paint, centerX, centerY + height, data.drawRadius, height, flipAngle,
                        data.drawStartAngle, data.drawSweepAngle, data.color, data.offsetDx, data.offsetDy, sliceSpace)
                } else {
                    canvas.draw3DArcTop(paint, centerX, centerY, data.drawRadius, chart.innerRadius, flipAngle, data.drawStartAngle,
                        data.drawSweepAngle, data.color, data.offsetDx, data.offsetDy, sliceSpace)
                }
            }
        }
    }

    /**
     * 获取给定起始角度及角度范围内获取最小Y轴坐标的角度
     */
    private fun getMinYAngle(startAngle: Float, sweep: Float): Float {
        val start = startAngle.to360Range
        val end = (startAngle + sweep).to360Range

        if ((start <= 270 && start + sweep >= 270) || (start > 270 && start + sweep >= 630)) {
            // 起始调度+角度范围覆盖了270°
            return 270f
        }

        return if (start > 270) {
            // start 在270~360之间，不含270°
            // 结束角度在270~360及0~270之间，不含270°
            if (end > 270 || end < 180) {
                start
            } else if (270 - end > start - 270) {
                start
            } else {
                end
            }
        } else if (start >= 0 && start < 90) {
            // 结束角度在0~270之间，不含270°
            if (end >= 180) {
                end
            } else if (end < 90) {
                start
            } else if (end - 90 > 90 - start) {
                end
            } else {
                start
            }
        } else {
            // 结束角度在90~270之间，不含270°
            end
        }
    }
}